
/**
 *
 * @author Russell
 */
public class RsaEncryption {

    private int p;
    private int q;
    private int d;
    private int e;
    private int n;
    private int[] cipherText;
    private int[] decryptedText;
    private byte[] plainText;
    private int plainTextLength;
    private int plain;
    private String binString;
    private String binStringD;
    private String cipherString;
    private String decryptedString;

    public RsaEncryption(int p, int q, int e, byte[] plainText) {
        this.p = p;
        this.q = q;
        this.e = e; // public key
        this.n = this.p * this.q;   // n = pq
        this.plainText = new byte[plainText.length];
        this.plainText = plainText;    // plaintext that was input
        this.plainTextLength = this.plainText.length;   // number of bytes to be encrypted
        this.cipherText = new int[this.plainTextLength];
        this.decryptedText = new int[this.plainTextLength];
        this.binString = Integer.toBinaryString(this.e); // binary format of e
        this.cipherString = "";
        this.decryptedString = "";

        System.out.println("RSA Encryption using p=" + p + " q=" + q + " e=" + e);
        
        if (this.e == 5) {
            this.d = 173; // private key
            this.binStringD = Integer.toBinaryString(this.d);
        }
        if (this.e == 3) {
            this.d = 147; // private key 
            this.binStringD = Integer.toBinaryString(this.d);
        }
                
        
        RsaAlgorithm();
        System.out.println("RSA Decryption using p=" + p + " q=" + q + " d=" + d);
        //System.out.println(this.binStringD);
        RsaDecryption();
    }

    public void RsaDecryption(){
        //m = c^d mod n
         int powerOf = 2;
         int exp = this.d;
         int length = this.binStringD.length();   // length of e's binstring

         System.out.print(" Output of Decryption per RSA using ASCII int value is: ");
        for (int j = 0; j < this.cipherText.length; j++) {      // loop through each byte individually to encrypt
            int cipherInt = this.cipherText[j];  // int per RSA method
            System.out.print(cipherInt + ",");
            int counter = 0;    
            int temp = cipherInt * cipherInt;   // do first square outside of loop
            int last = temp % this.n;     // find first mod outside of loop
            if(this.binStringD.charAt((1))=='1'){
                powerOf = powerOf + 1;
                temp = last * cipherInt;
                last = temp % this.n;
            }
            //System.out.println("round " + j + " n = " + this.n + " squared = " + temp + " mod n = " + last);  // print result for troubleshooting
            int type;
            for (int i = 2; i < length; i++) {         // using binary string, use shift operations and if next digit is a 1, then last*number
                    powerOf = powerOf * 2;
                    temp = last * last;
                    last = temp % this.n;
                if (this.binStringD.charAt(i) == '1') {
                    powerOf = powerOf + 1;
                    temp = last * cipherInt;
                    last = temp % this.n;
                    type = 1;                         
                } else {   // else, last^2
                    type = 0;
                }
              //  System.out.println(" squared = " + temp + " mod n = " + last + " type = " + type + " power = " + powerOf);
            }
            this.decryptedText[j] = last;
            this.decryptedString = this.decryptedString + (char) last;
        }
        System.out.print("\n plainText as ASCII int value: ");
        for (int i=0; i<this.decryptedText.length; i++){
        System.out.print(this.decryptedText[i] + ",");
        }
        System.out.println("\nDecrypted cleartext is: " + this.decryptedString);
        System.out.print("\n");       
    }
    
    public void RsaAlgorithm() {
        //c= m^e mod n; c = plaintext^e mod n;
         int exp = this.e;
         int length = this.binString.length();   // length of e's binstring

         System.out.print(" Input per RSA using ASCII int value is: ");
        for (int j = 0; j < this.plainTextLength; j++) {      // loop through each byte individually to encrypt
            this.plain = new Integer(this.plainText[j]);   // cast as int per RSA method
            System.out.print(this.plain + ",");
            int counter = 0;    
            int temp = this.plain * this.plain;   // do first square outside of loop
            int last = temp % this.n;     // find first mod outside of loop
            //System.out.println("round " + j + " n = " + this.n + " squared = " + temp + " mod n = " + last);  // print result for troubleshooting
            int type;
            for (int i = 1; i < length; i++) {         // using binary string, use shift operations and if next digit is a 1, then last*number
                if (this.binString.charAt(length - i - 1) == '1') {
                    temp = last * this.plain;
                    last = temp % this.n;
                    type = 1;
                } else {   // else, last^2
                    temp = last * last;
                    last = temp % this.n;
                    type = 0;
                }
                //System.out.println(" squared = " + temp + " mod n = " + last + " type = " + type);
            }
            this.cipherText[j] = last;
            this.cipherString = this.cipherString + (char) last;
        }
        System.out.print("\n Ciphertext as ASCII int value: ");
        for (int i=0; i<this.cipherText.length; i++){
        System.out.print(this.cipherText[i] + ",");
        }
        System.out.println("\nCiphertext Stream is: " + this.cipherString);
        System.out.print("\n");
    }
}
